/*
TreeList

Single level headers only.
*/

//Retrieve the path where the tree images resident.
var __TreeList_img_path = function() {
	var get_path_id = "get_path_" + Math.random();
	document.write("<script id='" + get_path_id + "'></script>");
	var src = document.getElementById(get_path_id)
		.previousSibling.src;
	var idx = src.lastIndexOf('/');
	if (idx >= 0)
		src = src.substring(0, idx);
	return src;
}();

//Tree list expandable image.
var __TreeList_fn_plus = "" + __TreeList_img_path + "/tree_plus.gif";
//Tree list collapsable image.
var __TreeList_fn_minus = "" + __TreeList_img_path + "/tree_minus.gif";
//Tree list blank image.
var __TreeList_fn_blank = "" + __TreeList_img_path + "/blank.gif";

/**
 * Tree list constructor.
 * It's parent node must has an attribute named dataSource (case sensitive).
 * @param parentNode In which DOM node this tree list resident. A DOM node or it's ID.
 */
function TreeList(parentNode) {
	//Get parent node.
	this.pNode = parentNode;
	if (typeof(this.pNode) == "string")
		this.pNode = document.getElementById(this.pNode);
	if (!this.pNode) throw "You must specify a parent DOM node.";
	var pNode = this.pNode;
	//Get data source.
	eval("this.dataSource = " + pNode.getAttribute("dataSource") + ";");
	if (!this.dataSource) throw "You must specify a data source.";
	var dataSource = this.dataSource;
	
	//Create the table.
	this.table = document.createElement("table");
	var tbl = this.table;
	tbl.className = dataSource.className;
	tbl.cellSpacing = 0;
	tbl.cellPadding = 0;
	
	pNode.appendChild(tbl);
	//Build tree list header.
	this.__appendHeader(tbl, dataSource.header);
	
	//_total is used to identify global odd/even rows.
	this._total = 0;
	//Build tree list body.
	this.__appendBody(tbl, dataSource.header.columns, dataSource.body);
}

//Build header.
TreeList.prototype.__appendHeader = function(tbl, header) {
	var thead = document.createElement("thead");
	thead.className = header.className;
	var cols = header.columns;
	tbl.appendChild(thead);
	var tr = document.createElement("tr");
	thead.appendChild(tr);
	tr.className = header.rowClass;
	//Append column headers(column titles).
	for (var i = 0; i < cols.length; i ++) {
		var col = cols[i];
		var th = document.createElement("th");
		tr.appendChild(th);
		th.innerHTML = col.innerHTML;
		th.className = col.className || header.columnClass;
		if (col.width)
			th.width = "" + col.width + "px";
	}
	tbl.thead = thead;
};

//Build body.
TreeList.prototype.__appendBody = function(tbl, cols, body) {
	var tbody = document.createElement("tbody");
	tbody.className = body.className;
	tbody.body = body;
	tbl.appendChild(tbody);
	this.__appendRows(tbody, tbody, 0, body.items);
	tbl.tbody = tbody;
};

//Append rows to parent row.
TreeList.prototype.__appendRows = function(tbody, prow, lvl, items) {
	if (!prow.childRows)
		prow.childRows = new Array();
	for (var i = 0; i < items.length; i ++) {
		this._total ++;
		var itm = items[i];
		//Create node row.
		var tr = tbody.insertRow(this.__getNewRowIndex(prow));
		tr.className = itm.className || (this._total % 2 == 0 ? tbody.body.oddRowClass : tbody.body.evenRowClass);
		tr.item = itm;
		itm.row = tr;
		tr.level = lvl;
		tr.parentRow = prow;
		prow.childRows.push(tr);
		var hasChildren = itm.items && itm.items.length || itm.asyncFetch;
		//Create tree node cell.
		var td = tr.insertCell(-1);
		td.className = itm.cells[0].className || tbody.body.cellClass;
		var tdspn = document.createElement("span");
		td.appendChild(tdspn);
		tdspn.style.marginLeft = "" + (lvl * 16) + "px";
		var icn = document.createElement("img");
		icn.style.verticalAlign = "middle";
		if (hasChildren) {
			icn.src = itm.asyncFetch ? __TreeList_fn_plus: __TreeList_fn_minus;
			var me = this;
			this.__addEventListener(icn, "click", this.__icon_clicked(this, icn));
			tr.expand = function(event_src) {
				return function() {
					me.__expand(event_src, false);
				};
			}(tr);
			tr.collapse = function(event_src) {
				return function() {
					me.__collapse(event_src);
				};
			}(tr);
		} else {
			icn.src = __TreeList_fn_blank;
		}
		icn.style.width = "16px";
		icn.style.height = "16px";
		icn.row = tr;
		tr.icn = icn;
		tdspn.appendChild(icn);
		var img = document.createElement("img");
		img.style.verticalAlign = "middle";
		img.src = __TreeList_fn_blank;
		img.style.width = "16px";
		img.style.height = "16px";
		img.className = itm.iconClass;
		tr.img = img;
		tdspn.appendChild(img);
		var spn = document.createElement("span");
		spn.innerHTML = itm.cells[0].innerHTML || "&nbsp;";
		spn.style.verticalAlign = "middle";
		tdspn.appendChild(spn);
		spn.row = tr;
		tr.nodeSpan = spn;
		//Create cells other than tree node cell.
		for (var j = 1; j < this.dataSource.header.columns.length; j ++) {
			var td = tr.insertCell(-1);
			if (j < itm.cells.length) {
				var cell = itm.cells[j];
				td.className = cell.className || tbody.body.cellClass;
				td.innerHTML = cell.innerHTML || "&nbsp;";
			} else {
				td.className = tbody.body.cellClass;
				td.innerHTML = "&nbsp;";
			}
		}
		if (this.dataSource.rowAdded) {
			this.dataSource.rowAdded(tr);
		}
		//Append children recursively.
		if (hasChildren) {
			this.__appendRows(tbody, tr, lvl + 1, itm.items);
			tr.expanded = true;
		}
	}
};

TreeList.prototype.__getNewRowIndex = function(prow) {
	if (!prow) return -1;
	if (typeof (prow.rowIndex) == "undefined") {
		return -1;
	}
	function getRowCount(r) {
		if (!r || !r.childRows)
			return 0;
		var len = r.childRows.length;
		for (var i = 0; i < r.childRows.length; i ++) {
			len += getRowCount(r.childRows[i]);
		}
		return len;
	}
	return prow.rowIndex + getRowCount(prow) + 1 - this.table.thead.rows.length;
};

TreeList.prototype.updateRow = function(row, item) {
	if (!item)
		return;
	//TODO Update icons.
	row.nodeSpan.innerHTML = item.cells[0].innerHTML || "&nbsp;";
	for (var i = 1; i < row.cells.length; i ++) {
		if (i < item.cells.length) {
			row.cells[i].innerHTML = item.cells[i].innerHTML || "&nbsp;";
			//TODO Update cell classes.
		} else {
			row.cells[i].innerHTML = "&nbsp;";
		}
	}
};

TreeList.prototype.removeRow = function(row) {
	if (row.childRows && row.childRows.length) {
		for (var i = 0; i < row.childRows.length; i ++) {
			this.removeRow(row.childRows[i]);
		}
	}
	for (var i = 0; i < row.parentRow.childRows.length; i ++) {
		if (row.parentRow.childRows[i] == row) {
			row.parentRow.childRows.splice(i, 1);
			break;
		}
	}
	this.table.deleteRow(row.rowIndex);
};

//Function used to append events to a DOM node.
TreeList.prototype.__addEventListener = function(ctl, eventName, handler) {
	if (ctl == null) return;
	if (ctl.attachEvent) {
		ctl.attachEvent("on" + eventName, handler);
	} else if (ctl.addEventListener) {
		ctl.addEventListener(eventName, handler, false);
	} else {
		ctl["on" + eventName] = handler;
	}
};

//Function to generate icon click event handler.
TreeList.prototype.__icon_clicked = function(me, icn) {
	return function() {
		me.__toggle_visibility(icn);
	};
};

//Toggle row expand/collapse status.
TreeList.prototype.__toggle_visibility = function(icn) {
	var tr = icn.row;
	if (icn.src.indexOf(__TreeList_fn_plus) >= 0) {
		tr.expand(false);
	} else {
		tr.collapse(false);
	}
};

TreeList.prototype.__showChildren = function(tr) {
	if (!tr.childRows) return;
	for (var i = 0; i < tr.childRows.length; i ++) {
		var ctr = tr.childRows[i];
		ctr.style.display = "";
		if (ctr.expanded) {
			this.__showChildren(ctr);
		}
	}
};

//Expand child nodes.
TreeList.prototype.__expand = function(tr, recursive) {
	if (tr.item.asyncFetch) {
		tr.item.asyncFetch = false;
		this.__fetchChildren(tr.item);
	} else {
		if (!tr.childRows) return;
		for (var i = 0; i < tr.childRows.length; i ++) {
			var ctr = tr.childRows[i];
			ctr.style.display = "";
			if (recursive) {
				this.__expand(ctr, recursive);
			} else {
				this.__showChildren(tr);
			}
		}
		tr.expanded = true;
		tr.icn.src = __TreeList_fn_minus;
	}
};

//Collapse child nodes.
TreeList.prototype.__collapse = function(tr, recursive) {
	if (!tr.childRows) return;
	for (var i = 0; i < tr.childRows.length; i ++) {
		var ctr = tr.childRows[i];
		ctr.style.display = "none";
		if (ctr.expanded || recursive) {
			this.__collapse(ctr, recursive);
		}
	}
	tr.expanded = false;
	tr.icn.src = __TreeList_fn_plus;
};

//Expand all tree nodes, recursively.
TreeList.prototype.expandAll = function() {
	for (var i = 0; i < this.table.tbody.childRows.length; i ++) {
		var r = this.table.tbody.childRows[i];
		this.__expand(r, true);
	}
};

//Collapse all tree nodes, recursively.
TreeList.prototype.collapseAll = function() {
	for (var i = 0; i < this.table.tbody.childRows.length; i ++) {
		var r = this.table.tbody.childRows[i];
		this.__collapse(r, true);
	}
};

//Fetch children dynamically
TreeList.prototype.__fetchChildren = function(item) {
	xhr.doGet({
		url: item.fetchUrl || this.dataSource.body.fetchUrl,
		params: item.params,
//		onError: function(data) {
//			alert(data);
//		},
		onOK: this.__childrenFetched(this, item)
	});
};

TreeList.prototype.__childrenFetched = function(me, item) {
	return function(data) {
		eval("item.items = " + data + ";");
		var tr = item.row;
		me.__appendRows(me.table.tbody, tr, tr.level + 1, item.items);
		tr.expand(false);
	};
};
